/* 
* date:    2024-07-19
* version: v3.0.2
* author:  zijian666
*/
((name, root) => {
    const { console, imports } = (function (root) {
        if (typeof root.imports === "undefined") {
            const eventTarget = root instanceof EventTarget ? root : new EventTarget();
            const imports = () => { };
            imports.requestIdleCallback = typeof requestIdleCallback === "function" ? (fn, o) => requestIdleCallback(fn, o)
                : typeof root.requestIdleCallback === "function" ? (fn, o) => root.requestIdleCallback(fn, o)
                    : (fn, o) => setTimeout(() => {
                        const begin = Date.now();
                        fn({
                            timeRemaining: () => Math.max(0, 50 - (Date.now() - begin)),
                            didTimeout: true
                        });
                    }, Math.max(o && o.timeout, 50));

            Object.defineProperty(root, "imports", {
                configurable: false,
                enumerable: false,
                writable: false,
                value: new Proxy(imports, {
                    get: (target, prop) => target[prop],
                    set(target, prop, value) {
                        target.hasOwnProperty(prop) && console.warn(`import: ${prop} has been replaced!`, { old: target[prop], new: value });
                        target[prop] = value;
                        eventTarget.dispatchEvent(new CustomEvent("import", { detail: { name, value } }));
                        return true;
                    },
                    apply(target, _, argumentsList) {
                        const callback = argumentsList[1];
                        if (typeof callback !== "function") return;
                        const dependencies = [].concat(argumentsList[0]);
                        const once = argumentsList[2] === true ? new AbortController() : null;
                        function onimport(e) {
                            if (dependencies.some(name => !target.hasOwnProperty(name))) return false;
                            if (!e || !e.detail || !e.detail.name || !dependencies.includes(e.detail.name)) return false;
                            once && once.abort();
                            imports.requestIdleCallback(() => callback.apply(target, dependencies.map(name => target[name])), { timeout: 100 });
                            return true;
                        }
                        eventTarget.addEventListener("import", onimport, { signal: once ? once.signal : undefined });
                        onimport();
                    },
                })
            });
        }

        addEventListener("unhandledrejection", e => e.reason && e.reason.cause === false && e.preventDefault());

        function canWrite(console, name, level) {
            if (console == null) return false;
            if (typeof console.canWrite === "function") return console.canWrite(name, level);
            if (level == "error") return true;
            return Array.isArray(console.debugs) && console.debugs.some(x => x === name || x === "*");
        }

        return {
            console: new Proxy(root.console || console || { canWrite: () => false }, {
                get(target, prop) {
                    const value = target[prop];
                    if (typeof value !== "function" || canWrite(target, name, prop) === true) return value;
                    return () => { };
                }
            }),
            imports: root.imports,
        }
    })(root || this);

    if (imports[name]) {
        return;
    }

    function str(s) {
        switch (typeof s) {
            case "undefined": return "";
            case "boolean": return s ? "true" : "";
            case "string": return s.trim();
            case "number": return s.toString();
            case "object": return str(s || "");
            case "function": return str(s());
            default: return "";
        }
    }

    //在字符串中查找指定的字符,如果不存在返回字符串长度
    function indexOfAny(str, chars) {
        var r = str.length;
        for (var i = 0; i < chars.length; i++) {
            var j = str.indexOf(chars[i]);
            if (j >= 0 && r > j)
                r = j;
        }
        return r;
    }

    //处理url中的 \..
    function backtrack(path) {
        while (path !== (path = path.replace(/((\/|^)[^\/]+\/\.\.)|^\/\.\.|^\.\./g, ""))) {
        }
        return path;
    }

    function Params(search) {
        function buildValue(value) {
            if (value == null) {
                return null;
            }
            try {
                value = decodeURIComponent(value).replace(/\+/g, " ");
            } catch (e) {

            }
            try {
                var obj = JSON.parse(value);
                obj.toString = () => value;
                return obj;
            } catch (e) {

            }
            //const time = Date.parse(value, true)
            //if (isFinite(time)) {
            //    return new Date(time);
            //}
            return value;
        }

        function Item(name, value) {
            const m = /\[(\d+)\]$/.exec(name);
            const index = m ? parseInt(m[1]) || -1 : null;

            this.value = value;
            this.name = index == null ? name : name.slice(0, name.indexOf('['));
            this.index = index;
        }

        function eachParams() {
            if (search.charAt(0)) {
                search = search.substr(1);
            }
            return search.split("&").map(str => {
                const arr = str.replace(/=/, '\0').split('\0');
                const value = buildValue(arr[1]) || buildValue(arr[0]);
                const name = arr.length == 1 ? "" : decodeURIComponent(arr[0]);
                return [name, value];
            });
        }

        if (search.length > 1) {

            const params = typeof URLSearchParams === "undefined"
                ? eachParams()
                : new URLSearchParams(search);

            for (let p of params) {
                const item = new Item(p[0], p[1]);
                let old = this[item.name];

                if (old != null || item.index != null) {
                    if (old == null) {
                        this[item.name] = old = [];
                    } else if (!(old instanceof Array)) {
                        this[item.name] = old = [old];
                    }
                    if (item.index == null || item.index < 0) {
                        item.index = old.length;
                    }
                }

                if (item.index != null) {
                    old[item.index] = item.value;
                } else {
                    this[item.name] = item.value;
                }
            }
        }
    }

    /**
     * style:
     * 0|null       常规风格
     * 1|"brackets" 方括号风格
     * 2|"json"     JSON风格
     * */
    function urlencoded(params, style) {
        style = [null, "brackets", "json"][style] || style;

        if (params == null || Object.keys(params).length === 0) {
            return "";
        }
        var arr = [];
        (function (value, name) {
            if (value == null) {
                return;
            }
            if (name == null) {
                name = "";
            }
            switch (typeof value) {
                case "number":
                case "boolean":
                    arr.push(name + "=" + value);
                    break;
                case "string":
                    if (value) {
                        arr.push(name + "=" + encodeURIComponent(value));
                    } else {
                        arr.push(name);
                    }
                    break;
                case "object":
                    if (value instanceof Array) {
                        if (style === "json" && name.length > 0) {
                            arr.push(name + "=" + encodeURIComponent(JSON.stringify(value)));
                        } else {
                            name = style == "brackets" ? (name.length > 0 ? name + "%5B%5D" : "") : name;
                            for (let item of value) {
                                arguments.callee(item || "", name);
                            }
                        }
                        return;
                    }
                    var keys = Object.keys(value);
                    if (keys.length > 0) {
                        if (style === "json" && name.length > 0) {
                            arr.push(name + "=" + encodeURIComponent(JSON.stringify(value)));
                            return;
                        }
                        for (let key of keys) {
                            var val = value[key];
                            if (val == null) {
                                val = "";
                            }
                            let prop = encodeURIComponent(key);
                            prop = style == "brackets" ? (name.length > 0 ? name + "%5B" + prop + "%5D" : prop) : (name.length > 0 ? name + "." + prop : prop);
                            arguments.callee(val, prop);
                        }
                    } else if (value instanceof Date) {
                        var date = value.getFullYear()
                            + "-" + ("0" + (date.getMonth() + 1)).slice(-2)
                            + "-" + ("0" + value.getDate()).slice(-2)
                            + " " + ("0" + value.getHours()).slice(-2)
                            + "%3A" + ("0" + value.getMinutes()).slice(-2)
                            + "%3A" + ("0" + value.getSeconds()).slice(-2);
                        arr.push(name + "=" + date);
                    } else {
                        arr.push(name + "=" + encodeURIComponent(value.toString()));
                    }
                    break;
                //case "function":
                //if (value.length == 0) {
                //    arguments.callee(value(), name);
                //}
                //break;
            }
        })(params, "");
        return arr.join("&");
    }


    Params.prototype.toJSON = function () {
        return JSON.stringify(Object.assign({}, this));
    };

    Params.prototype.toString = function () { return urlencoded(this); };

    const token = new Object();

    function Url(url) {
        if (arguments[1] !== token) {
            return new Url(url, token);
        }
        var me = this;
        url = str(url || window.location.href);

        var _scheme = null; // http://
        var _domain = null; // baidu.com
        var _path = null;   // /a/b/c
        var _query = null;  // ?id=1
        var _anchor = null; // #abc

        var error = function (name, value) {
            throw new Error('The "' + name + '" format is invalid. from string : "' + value + '"');
        }

        var prop = {
            scheme: {
                get: function () { return _scheme; },
                set: function (value) {
                    value = str(value);
                    if (/^([a-z]+:)?\/\/$/.test(value) === false) {
                        error("scheme", value);
                    }
                    _scheme = value;
                },
                configurable: false,
                enumerable: true,
            },
            domain: {
                get: function () { return _domain; },
                set: function (value) {
                    value = str(value);
                    if (/^[a-zA-Z0-9:\.\-]+$/.test(value) === false) {
                        error("domain", value);
                    }
                    if (value.slice(-1) === "/") {
                        value = value.slice(0, -1);
                    }
                    _domain = value;
                },
                configurable: false,
                enumerable: true,
            },
            path: {
                get: function () {
                    var path = _path;
                    if (_scheme == null || _scheme === "" || _domain == null || _domain !== "") {
                        return path;
                    }
                    path = backtrack(path);
                    if (path.charAt(0) !== "/") {
                        path = "/" + path;
                    }
                    return path;
                },
                set: function (value) {
                    value = str(value).replace(/([^\/]|^)[\/]{2,}/g, function (m) { return m.charAt(0) === ":" ? "://" : m.charAt(0) + "/"; });
                    if (/^\/?(([^\/?#]+)(\/|$))+$/.test(value) === false) {
                        error("path", value);
                    }
                    _path = value;
                },
                configurable: false,
                enumerable: true,
            },
            query: {
                get: function () {
                    var str = urlencoded(me.params);
                    return (str === "") ? "" : "?" + str;
                },
                set: function (value) {
                    _query = value;
                    me.params = new Params(value);
                },
                configurable: false,
                enumerable: true,
            },
            anchor: {
                get: function () { return (_anchor === "" || _anchor.charAt(0) === "#") ? _anchor : "#" + _anchor; },
                set: function (value) { _anchor = str(value); },
                configurable: false,
                enumerable: true,
            }
        };


        if (url.length > 0) {

            var scheme = /^([^:/]+:\/\/|\/\/)/.exec(url) || "";
            if (scheme && scheme.length > 0) {
                prop.scheme.set(scheme[0]);
                url = url.substr(_scheme.length);
                prop.domain.set(url.substr(0, indexOfAny(url, ["/", "\\", "?", "#"])).replace(/[\/\\]$/g, ""));
                url = url.substr(_domain.length);
            }
            prop.path.set(url.substr(0, indexOfAny(url, ["?", "#"])).replace(/[?#]$/g, ""));
            url = url.substr(_path.length);
            prop.path.set(_path.replace(/(\\|\/)+/g, "/"))
            prop.query.set(url.substr(0, indexOfAny(url, ["#"])));
            url = url.substr(_query.length);
            prop.anchor.set(url);
        }


        if (typeof Object.defineProperties === "function") {
            Object.defineProperties(this, prop);
        } else {
            this.scheme = _scheme;
            this.domain = _domain;
            this.path = _path;
            this.query = _query;
            this.anchor = _anchor;
            this.params = parseSearch(_query);
        }


        this.toString = function () {
            var path = this.path;
            if (_scheme == null || _scheme === "" || _domain == null || _domain !== "") {
                path = backtrack(path);
                if (path.charAt(0) !== "/") {
                    path = "/" + path;
                }
            }
            return [this.scheme, this.domain, path, this.query, this.anchor === "#" ? "" : this.anchor].join("");
        };
    }

    function combine(url1, url2) {
        if (url1 == null || url1.length === 0) {
            return url2 && new Url(url2).toString();
        }
        if (url2 == null || url2.length === 0) {
            return new Url(url1).toString();
        }
        if (/^\.\.[^/]/.test(url2)) {
            url2 = "./" + url2;
        }
        var u1 = new Url(url1);
        var u2 = new Url(url2);

        if (u2.scheme != null) {
            if (u2.scheme === "//" && u1.scheme != null) {
                u2.scheme = u1.scheme;
            }
            return u2.toString();
        }

        if (u2.path != null && u2.path.length > 0) {
            if (u2.path.charAt(0) === "/") {
                u1.path = u2.path;
            }
            else if (u2.path.substr(0, 2) === "./") {
                u1.path = [u1.path, u2.path.substr(u1.path.slice(-1) === "/" ? 2 : 1)].join("");
            }
            else if (u1.path.slice(-1) === "/") {
                u1.path = [u1.path, u2.path].join("");
            } else if (u2.path.charAt(0) !== ".") {
                u1.path = [u1.path.substr(0, u1.path.lastIndexOf("/")), "/", u2.path].join("");
            }
        }

        if (u2.query == null || u2.query.length === 0) {
            if (u2.anchor != null && u2.anchor.length > 0) {
                u1.anchor = u2.anchor;
            } else {
                u1.query = "";
            }
            return u1.toString();
        }

        if (u2.query.substr(0, 2) === "?&") {
            if (u2.query.length > 2) {
                if (u1.query == null || u1.query.length === 0) {
                    u1.query = ["?", u2.query.substr(2)];
                } else {
                    u1.query = [u1.query, u2.query.substr(u1.query.slice(-1) === "&" ? 2 : 1)].join("");
                }
            }
        } else {
            u1.query = u2.query;
        }

        if (u2.anchor != null && u2.anchor.length > 0) {
            u1.anchor = u2.anchor;
        }

        return u1.toString();
    }

    Url.encoded = urlencoded;

    Url.combine = function () {
        if (arguments.length < 2) {
            return arguments[0];
        }
        var _base = arguments[0];
        for (var i = 1; i < arguments.length; i++) {
            _base = combine(_base, arguments[i]).toString();
        }
        return _base;
    };

    Url.parseSearch = x => new Params(x);

    Url.version = "3.0.2";

    location.params = new Proxy(Url.parseSearch(location.search), {
        get: (target, name) => target[name] || "",
        set: () => { },
    });

    root.Url = imports.Url = Url;

})("Url", window);
